#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "log.hpp"
#include "err.hpp"
#include "Task.hpp"
#include "GardLock.hpp"
#include "threadPool.hpp"

namespace ns_server
{
    static const uint16_t default_port = 8888;
    static const int backlog = 32;

    class tcp_server;

    struct thread_data
    {
        thread_data(int sock, const uint16_t &port, const std::string &ip, tcp_server *server)
            : sock_(sock), port_(port), ip_(ip), server_(server)
        {
        }

        int sock_;
        uint16_t port_;
        std::string ip_;
        tcp_server *server_;
    };

    class tcp_server
    {
    public:
        tcp_server(uint16_t port = default_port)
            : _port(port), quit(true)
        {
        }

        void InitServer()
        {
            // 1. 创建socket文件，监听文件描述符（从始至终只有一个）
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                // std::cerr << "create socket error: " << strerror(errno) << std::endl;
                LogMessage(Fatal, "create socket error, error: %d, reason: %s", errno, strerror(errno));
                exit(SOCK_ERR);
            }
            // std::cout << "create socket success : " << _listensock << std::endl;
            LogMessage(Bug, "create socket success");

            struct sockaddr_in server;
            socklen_t len = sizeof(server);
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = INADDR_ANY;
            // 2. 为服务器指明IP和端口号
            if (bind(_listensock, (struct sockaddr *)&server, len) < 0)
            {
                // std::cerr << "bind error :" << strerror(errno) << std::endl;
                LogMessage(Fatal, "bind error, error: %d, reason: %s", errno, strerror(errno));
                exit(BIND_ERR);
            }
            // std::cout << "bind success " << std::endl;
            LogMessage(Bug, "bind success");
            // 3. 监听
            if (listen(_listensock, backlog) < 0)
            {
                // std::cerr << "listen error :" << strerror(errno) << std::endl;
                LogMessage(Fatal, "listen error, error: %d, reason: %s", errno, strerror(errno));
                exit(LISTEN_ERR);
            }
        }

        void Start()
        {
            signal(SIGCHLD, SIG_IGN); // 忽略进程结束后发送的信号，并由OS自动回收进程

            quit = false;
            while (!quit)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                // 4. 接收连接，连接描述符（可以有很多个，每一个服务端都有一个）
                int sock = accept(_listensock, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    // std::cerr << "accept error :" << strerror(errno) << std::endl;
                    LogMessage(Fatal, "accept error, error: %d, reason: %s", errno, strerror(errno));
                    exit(ACCEPT_ERR);
                }

                std::string client_ip = inet_ntoa(client.sin_addr);
                uint16_t client_port = ntohs(client.sin_port);
                // std::cout << "获取新连接成功，" << sock << " from " << _listensock << std::endl;
                LogMessage(Bug, "获取新连接成功, %d form %d", sock, _listensock);

                // 5. 利用返回的fd进行业务处理 TODO
                // V1 阻塞式调用 只能接收一个客户端
                // Service(sock);

                // V2 多进程
                // pid_t id = fork();
                // if(id < 0) // 如果创建进程失败
                // {
                //     close(sock);
                //     continue;
                // }
                // else if(id == 0) // 子进程，父进程的fd，会被child继承吗？会。 父子会用同一张文件描述符表吗？不会，子进程拷贝继承父进程的fd table;
                // {
                //     close(_listensock); // 建议：关闭不需要的文件描述符，防止误操作
                //     if(fork() > 0) exit(0);
                //     // 子进程退出后，由孙子进程继续进程，并且变为孤儿进程，由OS来回收
                //     Service(sock);
                //     exit(0);
                // }

                // close(sock); // 必须关闭掉不需要的fd，否则会造成fd资源泄漏
                // waitpid(id, nullptr, WNOHANG);

                // V3 多线程
                // pthread_t th;
                // thread_data* data = new thread_data(sock, client_port, client_ip, this);
                // pthread_create(&th, nullptr, ThtreadRoutine, data);

                // V4 线程池
                Task t(sock, client_ip, client_port, std::bind(&tcp_server::Service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                ThreadPool<Task>::get_instance()->PushTask(t);
            }
        }

        static void *ThtreadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            thread_data *data = static_cast<thread_data *>(args);

            data->server_->Service(data->sock_, data->ip_, data->port_);

            delete data;
            return nullptr;
        }

        void Service(int sock, const std::string &client_ip, const uint16_t &client_port)
        {
            std::string who = client_ip + "-" + std::to_string(client_port);

            char buffer[1024];
            while (true)
            {
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << who << " Get message# " << buffer << std::endl;

                    std::string ret = "Get message success!";
                    write(sock, ret.c_str(), ret.size());
                }
                else if (n == 0)
                {
                    close(sock);
                    std::cout << "quit..." << std::endl;
                    break;
                }
                else
                {
                    close(sock);
                    std::cerr << "read error: " << strerror(errno) << std::endl;
                    exit(READ_ERR);
                }
            }
        }

        ~tcp_server()
        {
        }

    private:
        int _listensock;
        bool quit;
        uint16_t _port;
    };
}